package main

import (
	"fmt"
)

/*
切片本身是一个标准库中实现的一个特殊的结构体，这个结构体中有三个属性，分别代表数组指针、长度、容量。
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

切片的申明方式与声明数组的方式非常相似，与数组相比，切片不用声明长度:
var <slice name> []<type>
*/

func main() {

	// 方式1，声明并初始化一个空的切片
	var s1 []int = []int{}

	// 方式2，类型推导，并初始化一个空的切片
	var s2 = []int{}

	// 方式3，与方式2等价
	s3 := []int{}

	// 方式4，与方式1、2、3 等价，可以在大括号中定义切片初始元素
	s4 := []int{1, 2, 3, 4}

	// 方式5，用make()函数创建切片，创建[]int类型的切片，指定切片初始长度为0
	s5 := make([]int, 0)

	// 方式6，用make()函数创建切片，创建[]int类型的切片，指定切片初始长度为2，指定容量参数4
	s6 := make([]int, 2, 4)

	// 方式7，引用一个数组，初始化切片
	arr := [5]int{6, 5, 4, 3, 2}
	// 从数组下标2开始，直到数组的最后一个元素
	s7 := arr[2:]
	// 从数组下标1开始，直到数组下标3的元素，创建一个新的切片
	s8 := arr[1:3]
	// 从0到下标2的元素，创建一个新的切片
	s9 := arr[:2]

	fmt.Printf("s1 = %d\n", s1)
	fmt.Printf("s2 = %d\n", s2)
	fmt.Printf("s3 = %d\n", s3)
	fmt.Printf("s4 = %d\n", s4)
	fmt.Printf("s5 = %d\n", s5)
	fmt.Printf("s6 = %d\n", s6)
	fmt.Printf("s7 = %d\n", s7)
	fmt.Printf("s8 = %d\n", s8)
	fmt.Printf("s9 = %d\n", s9)

	change(s9)
	fmt.Printf("s9 = %d\n", s9)

	var nilSlice []int
	fmt.Println("nilSlice length:", len(nilSlice))
	fmt.Println("nilSlice capacity:", len(nilSlice))

	s10 := []int{9, 8, 7, 6, 5}
	fmt.Println("s10 length: ", len(s10))
	fmt.Println("s10 capacity: ", cap(s10))

	/*
		len(slice)：切片当前长度（元素个数）
		cap(slice)：切片当前容量（最多能容纳多少元素
	*/
	s := make([]int, 2, 5) // 长度2，容量5
	fmt.Println(s)
	fmt.Println(len(s)) // 输出 2
	fmt.Println(cap(s)) // 输出 5

	//切片添加元素
	s11 := []int{}
	fmt.Println("s11 = ", s11)

	src1 := []int{1, 2, 3}
	dst1 := make([]int, 4, 5)

	fmt.Println("before copy, src1 = ", src1)
	fmt.Println("before copy, dst1 = ", dst1)

	copy(dst1, src1)

	fmt.Println("before copy, src1 = ", src1)
	fmt.Println("before copy, dst1 = ", dst1)

	//添加元素
	add_val(s)

	//删除元素
	//	remove(s)

	s12 := []int{1, 2, 3}
	s13 := append(s12, 4, 5, 6, 7, 8, 9, 10) // 超过原容量，发生扩容

	fmt.Println(s12) // [1 2 3]
	fmt.Println(s13) // [1 2 3 4 5 6 7 8 9 10]
	//	fmt.Println(s12 == s13) // [1 2 3 4 5 6 7 8 9 10]

}

// 切片作为函数参数时是引用传递（实际上是结构体的值传递，但结构体里有指针），函数内修改会影响外部数据。
func change(arr []int) {
	arr[1] = 10086
}

func remove(s []int) {

	i := 2 // 要删除下标为2的元素（值为3）

	/*
		s[:i]：保留前 i 个元素（不包括第 i 个）
		s[i+1:]：保留第 i+1 个及以后的元素
		append(..., ...)：把两部分拼接起来
	*/
	s = append(s[:i], s[i+1:]...)

	fmt.Println(s) // 输出 [1 2 4 5]
}

func add_val(s []int) {
	// append函数追加元素
	s = append(s)
	s = append(s, 1)
	s = append(s, 2, 3)
	fmt.Println("s = ", s)
}
